﻿using System;
using System.Collections.Generic;
using System.Web;
using AMC.SubstitutionCache.Events;
using Orchard.DisplayManagement;
using Orchard.Logging;
using Orchard.Mvc;

namespace AMC.SubstitutionCache.Services
{
    /// <summary>
    /// Responsible for the initial rendering phase when a possible replacement shape has been found, firing events, and adding it to the current contexts 
    /// </summary>
    public class SubstitutionService : ISubstitutionService
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IEnumerable<ISubstitutionEvents> _shapeEventHandlers;

        public SubstitutionService(IHttpContextAccessor httpContextAccessor, IEnumerable<ISubstitutionEvents> shapeEventHandlers)
        {
            _httpContextAccessor = httpContextAccessor;
            _shapeEventHandlers = shapeEventHandlers;

            Logger = NullLogger.Instance;
        }

        public ILogger Logger { get; set; }

        public bool IsRenderingEnabled
        {
            get
            {
               return HttpContext.Items[SubstitutionConstants.SubstitutionRenderingEnabledKey] != null;
            }

            private set
            {
                if (value) 
                    HttpContext.Items[SubstitutionConstants.SubstitutionRenderingEnabledKey] = Guid.NewGuid();
                ////else 
                ////    HttpContext.Items[SubstitutionRenderingEnabledKey] = null;
            }
        }

        public Guid? RequestIdentifier
        {
            get
            {
                return HttpContext.Items[SubstitutionConstants.SubstitutionRenderingEnabledKey] as Guid?;
            }
        }

        private HttpContextBase HttpContext
        {
            get
            {
                var ctx = _httpContextAccessor.Current();

                if (ctx == null)
                    throw new HttpException("SubstitutionService requires a valid HttpContext");

                return ctx;
            }
        }

        private IDictionary<string, SubstitutionContext> SubstitutionContexts
        {
            get
            {
                var items = HttpContext.Items[SubstitutionConstants.SubstitutionItemsKey] as IDictionary<string, SubstitutionContext>;

                if (items == null)
                {
                    items = new Dictionary<string, SubstitutionContext>();
                    HttpContext.Items[SubstitutionConstants.SubstitutionItemsKey] = items;
                }

                return items;
            }
        }

        public SubstitutionContext this[string key]
        {
            get
            {
                if (SubstitutionContexts.ContainsKey(key))
                    return SubstitutionContexts[key];

                return null;
            }
        }

        public void EnableRendering()
        {
            if (!IsRenderingEnabled)
                IsRenderingEnabled = true;
        }

        public void TryAddToken(IShape shape)
        {
            try
            {
                if (!IsRenderingEnabled)
                    return;

                if (shape == null)
                    return;

                var context = new SubstitutionContext(shape)
                {
                    RequestId = RequestIdentifier
                };

                var addingContext = new SubstitutionAddingContext
                {
                    Shape = shape,
                    SubstitutionContext = context
                };

                foreach (var handler in _shapeEventHandlers)
                    handler.SubstitutionAdding(addingContext);

                if (!addingContext.ShouldAdd)
                    return;

                shape = addingContext.Shape;
                context = addingContext.SubstitutionContext;

                // add context to collection
                if (SubstitutionContexts.ContainsKey(context.Key))
                    throw new ArgumentException(string.Format("An ReplacementCacheContext with the same key '{0}' has already been added", context.Key), "shape");
            
                SubstitutionContexts.Add(context.Key, context);

                // add to shape
                shape.SetSubstitutionContext(context);

                // add wrapper
                shape.Metadata.Wrappers.Add(SubstitutionConstants.SubstitutionTokenWrapperName);

                var addedContext = new SubstitutionAddedContext
                {
                    Shape = shape,
                    SubstitutionContext = context
                };

                foreach (var shapeEvent in _shapeEventHandlers)
                    shapeEvent.SubstitutionAdded(addedContext);

            }
            catch (Exception ex)
            {
                Logger.Warning(ex, ex.Message);
            }
        }

        public IEnumerable<SubstitutionContext> Items()
        {
            return SubstitutionContexts.Values;
        }
    }
}